package com.rex;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rex.CustomConstant.*;

import java.util.Scanner;

/**
 * JSON转SOQL
 *
 * @author Rex
 * @since 2023/8/3 11:10
 */
public class JSON2SOQL {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String inputStr = input.next();
//        String inputStr = "{\"conditions\":[{\"conditions\":[{\"conditions\":[{\"conditions\":[{\"field\":\"Country_vod__c\",\"operator\":\"like\",\"values\":[\"'%a%'\"]},{\"field\":\"Email_vod__c\",\"operator\":\"notLike\",\"values\":[\"'%b%'\"]}],\"conjunction\":\"and\"},{\"conditions\":[{\"field\":\"City_vod__c\",\"operator\":\"equals\",\"values\":[\"'c'\"]},{\"field\":\"Name\",\"operator\":\"notEquals\",\"values\":[\"'d'\"]}],\"conjunction\":\"and\"}],\"conjunction\":\"or\"},{\"field\":\"Address_Line_1_vod__c\",\"operator\":\"lessThan\",\"values\":[\"'e'\"]}],\"conjunction\":\"and\"},{\"conditions\":[{\"field\":\"Address_Line_1_vod__c\",\"operator\":\"greaterThan\",\"values\":[\"'f'\"]},{\"field\":\"Address_Line_2_vod__c\",\"operator\":\"lessThanOrEqualTo\",\"values\":[\"'g'\"]}],\"conjunction\":\"and\"},{\"conditions\":[{\"field\":\"Call2_vod__r.Name\",\"operator\":\"greaterThanOrEqualTo\",\"values\":[\"'h'\"]},{\"field\":\"City_vod__c\",\"operator\":\"like\",\"values\":[\"'i%'\"]}],\"conjunction\":\"and\"}],\"conjunction\":\"or\"}";
//        String inputStr = "{\"conditions\":[{\"field\":\"Country_vod__c\",\"operator\":\"notLike\",\"values\":[\"'%a%'\"]},{\"field\":\"Account_vod__r.Name\",\"operator\":\"like\",\"values\":[\"'%b%'\"]},{\"field\":\"QA_Field_04__c\",\"operator\":\"greaterThanOrEqualTo\",\"values\":[\"2020-09-18T16:00:00.000Z\"]},{\"field\":\"QA_Field_02__c\",\"operator\":\"equals\",\"values\":[\"CNY1\"]}],\"conjunction\":\"and\"}";
        try {
            JSONObject jsonObject = JSON.parseObject(inputStr);
            check(jsonObject);
            System.out.println("The output SOQL is : " + convert(jsonObject, null, true));
        } catch (RuntimeException ex) {
            throw new RuntimeException(ex.getMessage());
        } catch (Exception ex) {
            throw new RuntimeException("please input the correct json");
        }
    }

    /**
     * convert
     *
     * @param jsonObject param
     * @return {@link String}
     * @author Rex
     * @since 2023/8/3 11:11
     */
    private static String convert(JSONObject jsonObject, String paramConjunction, boolean isRoot) {
        StringBuilder stringBuilder = new StringBuilder();
        StringBuilder stringBuilderConjunction = new StringBuilder();
        if (null != paramConjunction) {
            stringBuilderConjunction.append(Symbol.SPACE).append(paramConjunction).append(Symbol.SPACE);
        }
        String conjunction = jsonObject.getString(Key.CONJUNCTION).toUpperCase();
        JSONArray jsonArrayCond = jsonObject.getJSONArray(Key.CONDITIONS);
        int arrayLen = jsonArrayCond.size();
        for (int i = 0; i < arrayLen; i++) {
            JSONObject jo = JSON.parseObject(JSON.toJSONString(jsonArrayCond.get(i)));
            // if the node contains conjunction,then need recursion current node
            if (jo.containsKey(Key.CONJUNCTION)) {
                stringBuilder.append(convert(jo, i == 0 ? null : conjunction, false));
            } else {
                if (i > 0) {
                    stringBuilder.append(Symbol.SPACE).append(conjunction).append(Symbol.SPACE);
                }
                String operator = jo.getString(Key.OPERATOR);
                if (null == operator) {
                    throw new RuntimeException("please check the operator node");
                }
                OperatorEnums operatorEnums = OperatorEnums.getOperatorEnums(operator);
                if (operatorEnums == null) {
                    throw new RuntimeException("please check the operator, the operator can only be \"like\", \"notLike\", \"equals\", \"notEquals\", \"lessThan\", \"lessThanOrEqualTo\", \"greaterThan\", or \"greaterThanOrEqualTo\"");
                }
                JSONArray jsonArrayValues = jo.getJSONArray(Key.VALUES);
                if (null == jsonArrayValues) {
                    throw new RuntimeException("please check the values node, the values can not be null");
                }
                if (jsonArrayValues.size() != 1) {
                    throw new RuntimeException("please check the values, the values can only have one element");
                }
                String field = jo.getString(Key.FIELD);
                if (null == field) {
                    throw new RuntimeException("please check the field node, the field can not be null");
                }
                if (field.contains(" ")) {
                    throw new RuntimeException("please check the field node, the field can not have space");
                }
                String block = operatorEnums.getValuePrefix() +
                        field +
                        operatorEnums.getValueSuffix() +
                        jsonArrayValues.get(0);
                if (OperatorEnums.NOT_LIKE.getKey().equals(operatorEnums.getKey())) {
                    block = String.format("(%s)", block);
                }
                stringBuilder.append(block);
            }
        }
        String result;
        if (!isRoot) {
            result = String.format("(%s)", stringBuilder);
        } else {
            result = stringBuilder.toString();
        }
        return stringBuilderConjunction.append(result).toString();
    }

    /**
     * check
     *
     * @param jsonObject param
     * @author Rex
     * @since 2023/8/3 11:13
     */
    private static void check(JSONObject jsonObject) {
        if (jsonObject.size() == 0) {
            throw new RuntimeException("the input json can not be empty!");
        }
        checkNode(jsonObject);
    }

    /**
     * check node
     *
     * @param jsonObject param
     * @author Rex
     * @since 2023/8/3 11:13
     */
    private static void checkNode(JSONObject jsonObject) {
        if (!jsonObject.containsKey(Key.CONJUNCTION)) {
            throw new RuntimeException("please input the correct json with conjunction node");
        }
        String conjunction = jsonObject.getString(Key.CONJUNCTION).toUpperCase();
        if (!Conjunction.AND.equals(conjunction) && !Conjunction.OR.equals(conjunction)) {
            throw new RuntimeException("the conjunction can only be 'AND' or 'OR'");
        }
        if (!jsonObject.containsKey(Key.CONDITIONS)) {
            throw new RuntimeException("please input the correct json with conditions node");
        }
    }

}
